#include <windows.h>
#include <time.h>
#include <fstream>
#include <sstream>
#include <io.h>
#include "libind/bind.h"
#include "libind/gge.h"
#include "libind/appbuf.h"

#pragma comment(lib, "galaxy2d")
using namespace gge;

lua_State *L;
// CRITICAL_SECTION s_criticalSection;

ccc_bind_declspec void cc_msgbox(const char* title, const char* text) { MessageBox(nullptr, text, title, MB_CANCELTRYCONTINUE); }
int luaB_printAsc(lua_State *L) {
	int n = lua_gettop(L);  /* number of arguments */
	int i;
	for (i = 1; i <= n; i++) {  /* for each argument */
		size_t l;
		char *s = (char*)luaL_tolstring(L, i, &l);  /* convert it to string */
		if (l > 0 && s) {
			char c = s[l];
			s[l] = 0;
			auto p = (char*)Variant_Utf82Asc(s);
			s[l] = c;
			if (p) {
				s = p;
				l = strlen(p);
			}
		}
		if (i > 1)  /* not the first element? */
			lua_writestring("\t", 1);  /* add a tab before it */
		lua_writestring(s, l);  /* print it */
		lua_pop(L, 1);  /* pop result */
	}
	lua_writeline();
	return 0;
}

void registerMain(lua_State* ccc_register_state)
{
 	ccc_register_func(cc_msgbox);
}

#if defined(ccc_use_traceback)
int traceback_index;
static bool errored = false;
int traceback(lua_State *state)
{
	if (errored)
	{
		return 0;
	}
	errored = true;
	const char *msg = nullptr;
	if (lua_isstring(state, -1))
	{
		msg = lua_tostring(state, -1);
		lua_pop(state, 1);
	}
	luaL_traceback(state, state, msg, 1);
	if (lua_isstring(state, -1))
	{
		msg = lua_tostring(state, -1);
		lua_pop(state, 1);
	}
	if (msg != nullptr)
	{
		cc_printf(msg, true);
		cc_msgbox(__FUNCTION__, msg);
	}
	return 0;
}
#endif


HINSTANCE g_hInstance;
static bool readArgs(std::string& exepath, std::string& exename, std::string& mainlua);
static void args2lua(bool console, const std::string& exepath, const std::string& exename, const std::string& mainlua);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int)
{
	std::string exepath, exename, mainlua;
	readArgs(exepath, exename, mainlua);
	g_hInstance = hInst;
	srand(time(nullptr));
	gge::Random_Seed(time(nullptr));
//	InitializeCriticalSection(&s_criticalSection);
	L = ELuna::openLua();
	registerGGE(L);
	registerScheduler(L);
	registerMain(L);

	bool isConsole = AttachConsole(ATTACH_PARENT_PROCESS);
#ifdef _DEBUG
	isConsole = isConsole || AllocConsole();
#endif
	if(isConsole)
	{
		freopen("CONIN$", "r", stdin);
		freopen("CONOUT$", "w", stdout);
		freopen("CONOUT$", "w", stderr);
	}

	ccc_en_lock;
#if defined(ccc_use_traceback)
	lua_pushcfunction(L, traceback);
	traceback_index = lua_gettop(L);
#endif

	lua_newtable(L);

	args2lua(isConsole, exepath, exename, mainlua);

#if defined(ccc_use_traceback)
	auto r = luaL_loadfile(L, "app.lua");
	if (r == LUA_OK)
	{
		if (lua_pcall(L, 0, LUA_MULTRET, traceback_index) != LUA_OK)
		{
			auto msg = lua_tostring(L, -1);
			if (msg != nullptr)
			{
				std::ostringstream ss;
				ss << "top " << lua_gettop(L);
				cc_msgbox(ss.str().c_str(), msg);
			}
			lua_pop(L, 1);
		}
	}
#else
	if (0) {
		std::string str = "#include \"appbuf.h\"\n#include <fstream>\nstatic char appbuf[] = {\n";
		str += getAppStr("app.lua");
		str += "\n\n\n";
		str += getAppTxt("app.txt");
		std::ofstream ofile("libind/appbuf.cpp");
		if (ofile.is_open()) {
			ofile.write(str.c_str(), str.size());
			ofile.close();
		}
	}
//	luaL_dofile(L, "app.lua");
	luaL_dostring(L, getAppBuf());
#endif
	ccc_un_lock;

	gge::System_Start();
	ELuna::closeLua(L);
	gge::Engine_Release();
	return 0;
}

bool readArgs(std::string& exepath, std::string& exename, std::string& mainlua)
{
	exepath = __argv[0];
	int lastIndex = -1;
	for (int i = exepath.length() - 1; i >= 0; --i){
		auto& c = exepath[i];
		if (c == '\\'){
			c = '/';
		}
		if (c == '/' && lastIndex == -1){
			lastIndex = i + 1;
		}
	}
	if (lastIndex == -1){
		exename = exepath;
		exepath.clear();
	}
	else
	{
		exename = exepath.substr(lastIndex);
		exepath = exepath.substr(0, lastIndex);
	}
	if (__argc > 1) {
		mainlua = __argv[1];
		if (mainlua == "-") {
			mainlua = exepath + "main.lua";
		} else if (mainlua != "-") {
			bool isab = false;
			for (auto& c : mainlua) {
				if (c == '\\') {
					c = '/';
				} else if (c == ':') {
					isab = false;
				}
			}
			if (mainlua.find(".lua") == std::string::npos) {
				mainlua += ".lua";
			}
			if (!isab) {
				mainlua = exepath + mainlua;
			}
		}
	} else {
		mainlua = exepath + "main.lua";
	}
	if (::_access(mainlua.c_str(), 0) == 0) {
		return true;
	}
	mainlua.clear();
	return false;
}


void args2lua(bool console, const std::string& exepath, const std::string& exename, const std::string& mainlua)
{
	lua_pushboolean(L, console);
	lua_setfield(L, -2, "console");
#if _DEBUG
	lua_pushboolean(L, true);
#else
	lua_pushboolean(L, false);
#endif
	lua_setfield(L, -2, "debug");

	lua_pushstring(L, LUA_COPYRIGHT);
	lua_setfield(L, -2, "luaversion");

	lua_pushstring(L, "Galaxy2D Game Engine 4.2 dev 9");
	lua_setfield(L, -2, "ggeversion");

	if (exepath.empty()) {
		lua_pushstring(L, "");
	} else {
		lua_pushstring(L, Variant_Asc2Utf8(exepath.c_str()));
	}
	lua_setfield(L, -2, "exepath");

	lua_pushstring(L, exename.c_str());
	lua_setfield(L, -2, "exename");

	if (mainlua.empty()) {
		lua_pushstring(L, "");
	} else {
		lua_pushstring(L, Variant_Asc2Utf8(mainlua.c_str()));
	}
	lua_setfield(L, -2, "mainlua");

	//	lua_settable(L, -3);
	lua_setglobal(L, "__easy");
}